"""
用户 | 业务层
"""

import copy

import utils.enum_utils as enum
from components.global_param import get_user_id
from db.mongodb_db.transaction import MongoDBTransaction
from mapping import UserMapping
from models.po import (
    FilePO,
    UserPO,
    UserTokenPO,
)
from models.request import (
    UserChangePasswordRequest,
    UserCheckRequest,
    UserDetailRequest,
    UserLoginRequest,
    UserRegisterRequest,
    UserUpdateRequest,
)
from models.vo import (
    UserAuthenticatedVO,
    UserCheckVO,
    UserDetailVO,
)
from my_typing import *
from service import UserService
from utils.data_utils.special import ObjectIdStr
from utils.encrypt_utils import hash_with_sha256
from utils.exception_utils import BusinessError
from utils.validator_utils.common_validator import (
    is_valid_email,
    is_valid_phone,
)


class UserValidator:
    """ 用户 | 校验器 """

    union_fields: ListStr = ["username", "phone", "email"]
    """ 可以当做登录用户名的字段 """

    @staticmethod
    def is_exist_username(username: str) -> bool:
        """ 用户名 是否已存在 """

        return bool(UserService.get_one_by_username(username))

    @staticmethod
    def is_exist_nickname(
        nickname: str,
        user_id: ObjectIdStr | None = None,
    ) -> bool:
        """ 昵称 是否已存在（数据库中存在 & 不等于当前用户） """

        return bool(
            (user_po := UserService.get_one_by_nickname(nickname))
            and (
                user_id is None
                or user_id != user_po.id
            )
        )

    @staticmethod
    def is_exist_email(
        email: str,
        user_id: ObjectIdStr | None = None,
    ) -> bool:
        """ 邮箱 是否已存在（数据库中存在 & 不等于当前用户） """

        return bool(
            (user_po := UserService.get_one_by_email(email))
            and (
                user_id is None
                or user_id != user_po.id
            )
        )

    @staticmethod
    def is_exist_phone(
        phone: str,
        user_id: ObjectIdStr | None = None,
    ) -> bool:
        """ 手机号 是否已存在（数据库中存在 & 不等于当前用户） """

        return bool(
            (user_po := UserService.get_one_by_phone(phone))
            and (
                user_id is None
                or user_id != user_po.id
            )
        )

    @classmethod
    def is_exist_union_username(cls, union_username: str) -> bool:
        """ 用户名/手机号/邮箱 是否已存在 """

        return bool(UserService.get_one_by_union_username(union_username, cls.union_fields))

    @classmethod
    def validate_exist(cls, user_id: ObjectIdStr) -> None:
        """ 校验用户 ID 是否存在 """

        if not UserService.exists_by_id(user_id):
            raise BusinessError(f"用户 ID <{user_id}> 不存在")


class UserCheckHandler:
    """ 用户 | 检查 | 处理器 """

    @classmethod
    def check(cls, request: UserCheckRequest) -> UserCheckVO:
        """ 用户检查 """

        exist: bool = UserValidator.is_exist_union_username(request.union_username)

        return UserCheckVO(exist=exist)


class UserRegisterHandler:
    """ 用户 | 注册 | 处理器 """

    @staticmethod
    def validate_request(request: UserRegisterRequest) -> None:
        """
        校验请求参数

        1. 用户名是否存在
        2. 昵称是否存在
        3. 用密码和确认密码是否相同
        """

        error_list: ListStr = []

        if UserValidator.is_exist_username(request.username):
            error_list.append("用户名已存在")

        if UserValidator.is_exist_nickname(request.nickname):
            error_list.append("昵称已存在")

        if request.password != request.confirm_password:
            error_list.append("密码不一致")

        if error_list:
            raise BusinessError(error_list)

    @staticmethod
    def create_user(request: UserRegisterRequest) -> UserPO:
        """ 创建用户 """

        from biz.file_biz import FileDefaultHandler

        avatar_file_po: FilePO = FileDefaultHandler.get_default_avatar_file()
        hashed_password: str = hash_with_sha256(request.password)
        po: UserPO = UserPO(
            username=request.username,
            password=hashed_password,
            nickname=request.nickname,
            avatar_id=avatar_file_po.id,
        )
        UserService.insert_one(po)

        return po

    @classmethod
    def register(cls, request: UserRegisterRequest) -> UserAuthenticatedVO:
        """ 用户注册 """

        from biz.model_version_biz import VersionSetter
        from biz.user_token_biz import UserTokenHandler

        cls.validate_request(request)

        with MongoDBTransaction():
            user_po: UserPO = cls.create_user(request)
            user_token_po: UserTokenPO = UserTokenHandler.create_user_token(user_po.id)

        VersionSetter.async_create_version(
            new_po=user_po,
            model_operation=enum.ModelOperation.REGISTER,
            po_operation=enum.POOperation.CREATE,
        )

        return UserMapping.po_2_auth_vo(user_po, user_token_po)


class UserLoginHandler:
    """ 用户 | 登录 | 处理器 """

    @staticmethod
    def validate_request(request: UserLoginRequest) -> None:
        """
        校验请求参数

        1. 用户名/手机号/邮箱是否存在
        2. 密码是否正确
        """

        user_po: UserPO | None = UserService.get_one_by_union_username(
            union_username=request.union_username,
            union_fields=UserValidator.union_fields,
        )

        if not user_po:
            raise BusinessError("用户名不存在")

        hashed_password: str = hash_with_sha256(request.password)

        if user_po.password != hashed_password:
            raise BusinessError("密码错误")

    @staticmethod
    def _update_data(request: UserLoginRequest) -> tuple[UserPO, UserTokenPO]:
        """ 更新数据 """

        from biz.model_version_biz import VersionSetter
        from biz.user_token_biz import UserTokenHandler

        user_po: UserPO = UserService.get_one_by_union_username(request.union_username, UserValidator.union_fields)
        user_token_po: UserTokenPO = UserTokenHandler.get_or_create_user_token(user_po.id, request.token_max_age)
        VersionSetter.async_create_version(
            user_po, user_po,
            model_operation=enum.ModelOperation.LOGIN,
            po_operation=enum.POOperation.UPDATE,
            user_id=user_po.id,
        )

        return user_po, user_token_po

    @classmethod
    def login(cls, request: UserLoginRequest) -> UserAuthenticatedVO:
        """ 用户登录 """

        cls.validate_request(request)
        user_po, user_token_po = cls._update_data(request)

        return UserMapping.po_2_auth_vo(user_po, user_token_po)


class UserChangePasswordHandler:
    """ 用户 | 修改密码 | 处理器 """

    @staticmethod
    def validate_request(request: UserChangePasswordRequest) -> None:
        """
        校验请求参数

        1. 新旧密码是否相同
        2. 旧密码是否正确
        """

        if request.new_password == request.old_password:
            raise BusinessError("新旧密码不能相同")

        user_id: ObjectIdStr = get_user_id()
        user_po: UserPO = UserService.get_one_by_id(user_id)
        hashed_password: str = hash_with_sha256(request.old_password)

        if user_po.password != hashed_password:
            raise BusinessError("旧密码错误")

    @staticmethod
    def _update_password(
        user_po: UserPO,
        new_password: str,
    ) -> None:
        """ 更新密码 """

        hashed_password: str = hash_with_sha256(new_password)
        user_po.password = hashed_password
        UserService.update_one(user_po)

    @classmethod
    def _update_data(cls, request: UserChangePasswordRequest) -> UserPO:
        """ 更新数据 """

        from biz.model_version_biz import VersionSetter

        user_id: ObjectIdStr = get_user_id()
        user_po: UserPO = UserService.get_one_by_id(user_id)
        old_user_po: UserPO = copy.deepcopy(user_po)
        cls._update_password(user_po, request.new_password)
        VersionSetter.async_create_version(
            old_user_po, user_po,
            model_operation=enum.ModelOperation.CHANGE_PASSWORD,
            po_operation=enum.POOperation.UPDATE,
            user_id=user_po.id,
        )

        return user_po

    @classmethod
    def change_password(cls, request: UserChangePasswordRequest) -> UserDetailVO:
        """ 修改密码 """

        cls.validate_request(request)
        user_po: UserPO = cls._update_data(request)

        return UserMapping.po_2_detail_vo(user_po)


class UserGetter:
    """ 用户 | 获取器 """

    @staticmethod
    def get_detail(request: UserDetailRequest) -> UserDetailVO:
        """ 获取用户详情 """

        UserValidator.validate_exist(request.user_id)
        user_po: UserPO = UserService.get_one_by_id(request.user_id)

        return UserMapping.po_2_detail_vo(user_po)

    @staticmethod
    def get_user_by_avatar_id(avatar_id: ObjectIdStr) -> UserPO | None:
        """ 根据头像 ID 获取用户 """

        return UserService.get_one_by_avatar_id(avatar_id)


class UserUpdateHandler:
    """ 用户 | 更新 | 处理器 """

    @staticmethod
    def validate_request(request: UserUpdateRequest) -> None:
        """
        校验请求参数

        1. 头像是否为默认头像 or 当前用户的文件
        2. 邮箱是否合法
        3. 邮箱是否重复
        4. 昵称是否重复
        5. 手机号是否合法
        6. 手机号是否重复
        """

        from biz.file_biz import FileValidator

        avatar_id: ObjectIdStr = request.avatar_id
        email: StrOrNone = request.email
        nickname: str = request.nickname
        phone: StrOrNone = request.phone
        user_id: ObjectIdStr = get_user_id()
        error_list: ListStr = []

        is_my_file: bool = FileValidator.is_my_file(avatar_id, user_id)
        is_default_file: bool = FileValidator.is_default_file(avatar_id)

        if not (
            is_my_file
            or is_default_file
        ):
            error_list.append("头像只能使用当前用户的头像文件 / 默认头像文件")

        if UserValidator.is_exist_nickname(nickname, user_id):
            error_list.append("昵称已存在")

        if email:
            if not is_valid_email(email):
                error_list.append("邮箱格式错误")

            if UserValidator.is_exist_email(email, user_id):
                error_list.append("该邮箱已存在")

        if phone:
            if not is_valid_phone(phone):
                error_list.append("手机号格式错误")

            if UserValidator.is_exist_phone(phone, user_id):
                error_list.append("该手机号已存在")

        if error_list:
            raise BusinessError(error_list)

    @staticmethod
    def _update_data(request: UserUpdateRequest) -> UserPO:
        """ 更新数据 """

        from biz.model_version_biz import VersionSetter

        user_id: ObjectIdStr = get_user_id()
        user_po: UserPO = UserService.get_one_by_id(user_id)
        old_user_po: UserPO = copy.deepcopy(user_po)
        user_po.update_from_request(request)
        VersionSetter.async_create_version(
            old_user_po, user_po,
            model_operation=enum.ModelOperation.UPDATE,
            po_operation=enum.POOperation.UPDATE,
        )

        return user_po

    @classmethod
    def update(cls, request: UserUpdateRequest) -> UserDetailVO:
        """ 更新文件 """

        cls.validate_request(request)
        user_po: UserPO = cls._update_data(request)

        return UserMapping.po_2_detail_vo(user_po)
